package com.zy;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wanglx
 * @date  2020/07/09
 */
@SuppressWarnings({"unused", "FieldCanBeLocal"})
public class JsonConverter {
    private static ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    static private final String MUST_HEAD = "{\"bool\":{\"must\":[";
    static private final String MUST_TAIL = "]}}";
    static private final String SHOULD_HEAD = "{\"bool\":{\"should\":[";
    static private final String SHOULD_TAIL = "]}}";
    static private final String QUERY_HEAD = "{\"query\":";
    static private final String QUERY_TAIL = "}";

    static private final String MUST_NOT_TEMPLATE = "{\"bool\": {\"must_not\": [{\"term\": {\"_1\": {\"value\": \"_2\"}}}]}}";
    static private final String TERM_TEMPLATE = "{\"term\":{\"_1\":{\"value\":\"_2\"}}}";

    static class Parser {
        void parse(Node node, EsQueryNode esNode) {
            if (node == null || esNode == null) {
                return;
            }

            if (node.items != null && node.items.size() > 0) {
                Node lastChild = node.items.get(node.items.size() - 1);
                esNode.leaf = false;
                esNode.type = toType(getRelation(lastChild));
                esNode.nodes = new ArrayList<>();
                for (Node item : node.items) {
                    EsQueryNode esChild = new EsQueryNode();
                    esNode.nodes.add(esChild);
                    parse(item, esChild);
                }
            } else  {
                esNode.leaf = true;
                esNode.type = node.hasNotCondition == null || !node.hasNotCondition ? "term" : "must_not";
                if (node.data == null) {
                    throw  new  NodeParseException("数据解析错误: 数据节点中未包含data字段");
                }
                esNode.term = new Term();
                esNode.term.name = node.data.name;
                esNode.term.value = node.data.value;
            }
        }

        void  toDsl(EsQueryNode esNode,  StringBuilder builder) {
            if (esNode == null){
                return;
            }

            switch (esNode.type) {
                case "must": {
                    builder.append(MUST_HEAD);
                    traverse(esNode, builder);
                    builder.append(MUST_TAIL);
                    break;
                }
                case "should": {
                    builder.append(SHOULD_HEAD);
                    traverse(esNode, builder);
                    builder.append(SHOULD_TAIL);
                    break;
                }
                case "must_not": {
                    String tmp = MUST_NOT_TEMPLATE.replace("_1", esNode.term.name);
                    tmp = tmp.replace("_2", esNode.term.value);
                    builder.append(tmp);
                    break;
                }
                case "term": {
                    String tmp = TERM_TEMPLATE.replace("_1", esNode.term.name);
                    tmp = tmp.replace("_2", esNode.term.value);
                    builder.append(tmp);
                    break;
                }
            }
        }

        private void traverse(EsQueryNode esNode, StringBuilder builder) {
            for (int i = 0; i < esNode.nodes.size(); i++) {
                toDsl(esNode.nodes.get(i), builder);
                if (i != esNode.nodes.size() - 1 ) {
                    builder.append(",");
                }
            }
        }

        private String getRelation(Node lastChild) {
            String relation = lastChild.relationship;

            if (relation == null || relation.isEmpty()) {
                relation = lastChild.defaultRelationship;
            }

            if (relation == null ||  relation.isEmpty()) {
                throw new NodeParseException("数据解析错误: relationship 字段不存在");
            }
            return relation;
        }

        String toType(String  relation) {
            switch (relation){
                case "and":
                    return "must";
                case "or":
                    return "should";
                default:
                    throw new NodeParseException("不支持的关系类型：" + relation);
            }
        }
    }

    static class NodeParseException extends RuntimeException{
        NodeParseException(String message) {
            super(message);
        }
    }

    static class Data {
        String name;
        String title;
        String value;
        String value2;
        String operation;

        public void setName(String name) {
            this.name = name;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public void setValue2(String value2) {
            this.value2 = value2;
        }

        public void setOperation(String operation) {
            this.operation = operation;
        }
    }

    static class Node {
        String relationship;
        String defaultRelationship;
        Boolean hasNotCondition;
        Data data;
        List<Node> items;

        public void setRelationship(String relationship) {
            this.relationship = relationship;
        }

        public void setDefaultRelationship(String defaultRelationship) {
            this.defaultRelationship = defaultRelationship;
        }

        public void setData(Data data) {
            this.data = data;
        }

        public void setItems(List<Node> items) {
            this.items = items;
        }

        public void setHasNotCondition(Boolean hasNotCondition) {
            this.hasNotCondition = hasNotCondition;
        }
    }

    static  class Term {
        String name;
        String value;
    }

    static class EsQueryNode {
        String type; //must 、should 、must_not、term
        boolean leaf = false;
        Term term;
        List<EsQueryNode> nodes;
    }

    public static String jsonToDSL(String json) throws IOException {
        Node node =  mapper.readValue(json, Node.class );
        Parser parser = new Parser();
        EsQueryNode esQueryNode = new EsQueryNode();
        parser.parse(node, esQueryNode);

        StringBuilder builder = new StringBuilder();
        builder.append(QUERY_HEAD);
        parser.toDsl(esQueryNode, builder);
        builder.append(QUERY_TAIL);
        return builder.toString();
    }
}
